package com.secret.utils;

import javafx.scene.control.Alert;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;


/**
 * 不要修改这个类的代码实现了，这个类的加密解密，需要和dtools的类一致
 * AES的加密方式，它是线程安全的
 * 1. 加密方式：AES/CBC/PKCS5Padding
 * 2. 密钥长度：128位（16字节）
 * 3. 初始化向量（IV）：随机生成
 * 4. 加密模式：CBC模式
 * 5. 填充方式：PKCS5Padding
 * 6. 输出格式：Base64编码
 * 7. 字符集：UTF-8
 */
public class AesService {
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";


    private byte[] keyBytes;

    /**
     * aes key
     */
    private SecretKeySpec keySpec;


    public AesService(String key) {
        this.keyBytes = key.getBytes(StandardCharsets.UTF_8);

        this.keySpec = new SecretKeySpec(keyBytes, ALGORITHM);
    }


    /**
     * AES加密（CBC模式，PKCS5填充）
     *
     * @param plaintext 明文
     * @return Base64编码的密文字符串（包含IV）
     */
    public String encrypt(String plaintext) {
        byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);
        byte[] encrypted = encrypt(plaintextBytes);
        // 返回Base64编码的字符串
        return Base64.getEncoder().encodeToString(encrypted);
    }

    /**
     * AES加密（CBC模式，PKCS5填充）
     *
     * @param plaintext 明文
     * @return Base64编码的密文字符串（包含IV）
     */
    public byte[] encrypt(byte[] plaintext) {
        // 生成随机IV
        byte[] ivBytes = new byte[16];
        SecureRandom random = new SecureRandom();
        random.nextBytes(ivBytes);
        IvParameterSpec iv = new IvParameterSpec(ivBytes);

        // 初始化加密器
        byte[] ciphertext = null;
        try {
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, this.keySpec, iv);

            // 加密数据
            ciphertext = cipher.doFinal(plaintext);
        } catch (Exception e) {
            throw new RuntimeException("AES加密失败", e);
        }

        // 合并IV和密文
        byte[] combined = new byte[ivBytes.length + ciphertext.length];
        System.arraycopy(ivBytes, 0, combined, 0, ivBytes.length);
        System.arraycopy(ciphertext, 0, combined, ivBytes.length, ciphertext.length);

        return combined;
    }


    /**
     * AES解密（CBC模式，PKCS5填充）
     *
     * @param ciphertextBase64 Base64编码的密文字符串（包含IV）
     * @return 明文
     */
    public String decrypt(String ciphertextBase64) {
        // 解码Base64
        byte[] combined = Base64.getDecoder().decode(ciphertextBase64);
        byte[] plaintextBytes = decrypt(combined);
        return new String(plaintextBytes, StandardCharsets.UTF_8);
    }

    /**
     * AES解密（CBC模式，PKCS5填充）
     *
     * @param contentBytes 需要解密的内容（包含IV）
     * @return 明文
     */
    public byte[] decrypt(byte[] contentBytes) {

        // 提取IV和密文
        byte[] ivBytes = Arrays.copyOfRange(contentBytes, 0, 16);
        byte[] ciphertext = Arrays.copyOfRange(contentBytes, 16, contentBytes.length);
        IvParameterSpec iv = new IvParameterSpec(ivBytes);
        byte[] plaintextBytes = null;

        try {
            // 初始化解密器
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, this.keySpec, iv);

            // 解密数据
            plaintextBytes = cipher.doFinal(ciphertext);
        } catch (Exception e) {
            throw new RuntimeException("AES解密失败", e);
        }
        return plaintextBytes;
    }


    // ---------以下是AES 流式加密，解密的办法，避免加密解密大文件时，出现内存溢出--------------------------------------------------

    /**
     * AES加密（CBC模式，PKCS5填充）
     * 流式加密，避免内存溢出
     *
     * @param pin
     * @param pout
     * @throws Exception
     */
    public void encryptStream(InputStream pin, OutputStream pout) throws Exception {
        byte[] ivBytes = new byte[16];
        SecureRandom random = new SecureRandom();
        random.nextBytes(ivBytes);
        IvParameterSpec iv = new IvParameterSpec(ivBytes);

        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, this.keySpec, iv);

        OutputStream out = pout;
        InputStream in = pin;
        try(CipherOutputStream cos = new CipherOutputStream(out, cipher)){
            // 将IV写入文件头部
            out.write(iv.getIV());

            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                cos.write(buffer, 0, bytesRead);
            }
        }

    }


    /**
     * AES解密（CBC模式，PKCS5填充）
     * 流式解密，避免内存溢出
     *
     * @param in
     * @param out
     * @throws Exception
     */
    public void decryptStream(InputStream in, OutputStream out) throws Exception {
        InputStream fis = in;
        // 读取IV（前16字节）
        byte[] ivBytes = new byte[16];
        if (fis.read(ivBytes) != 16) {
            throw new IllegalStateException("IV读取失败");
        }
        IvParameterSpec iv = new IvParameterSpec(ivBytes);

        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.DECRYPT_MODE, this.keySpec, iv);

        OutputStream fos = out;
        try(CipherInputStream cis = new CipherInputStream(fis, cipher)){
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = cis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        }

    }


}
